UNPKG

typeorm-translatable

Version:

Translation classes, utils and decorators for i18n in TypeORM

215 lines (175 loc) 7.21 kB
# [typeorm-translatable](https://github.com/HanMoeHtet/typeorm-translatable) &middot; [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![npm version](https://img.shields.io/npm/v/typeorm-translatable.svg?style=flat)](https://www.npmjs.com/package/typeorm-translatable) Translation classes, utils, custom repositories and decorators for i18n in TypeORM. # Installation NPM ```bash npm install typeorm-translatable ``` Yarn ```bash yarn add typeorm-translatable ``` # Architecture This library is not supposed to be used for adding translation columns in the same table. Instead, a separate table is used for translation. For each source/target entity (table), a translation entity (table) must be created. The source entity has One-To-Many relation with the created translation entity. (e.g `Post` has many `PostTranslation`.) # Usage ```typescript import { TranslatableEntity, Translation } from 'typeorm-translatable'; import { PostTranslation } from './post-translation.entity'; import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm'; @Entity() export class Post extends TranslatableEntity<PostTranslation> { @PrimaryGeneratedColumn({ type: 'bigint', unsigned: true }) id?: number; @Column('varchar') title?: string; @Column('text') body?: string; @OneToMany( () => PostTranslation, postTranslation => postTranslation.source ) translations?: Translation<PostTranslation>[] | undefined; static translatableFields = new Set(['title', 'body']); } ``` ```typescript import { Post } from './post.entity'; import { TranslationEntity } from 'typeorm-translatable'; import { Column, Entity, ManyToOne } from 'typeorm'; @Entity() export class PostTranslation extends TranslationEntity<Post> { @Column('varchar') title?: string; @Column('text') body?: string; @ManyToOne( () => Post, post => post.translations ) source?: Post | undefined; } ``` For the target or source entity, extend `TranslatableEntity` class, add `translations` as one-to-many relation and add `translatableFields` static property. `translatableFields` must include fields that can be translated when using `entity.translate` method or `translateEntity` util function. For the translation entity (table that has translations), extend `TranslationEntity` class, define translatable columns and add `source` as many-to-one relation. Then when you retrieve data, left join translation entity. ```typescript let posts = await postRepository .createQueryBuilder('post') .leftJoinAndSelect( 'post.translations', 'translation', `translation.locale = :locale`, { locale, } ) .getMany(); ``` Output: ```json [ { "id": "1", "title": "This is a title", "body": "This is a post", "translations": [ { "id": "1", "locale": "my", "title": "ခေါင်းစဉ်", "body": "အကြောင်းအရာ" } ] } ] ``` For the above output, You may use `entity.translate` method to replace `title` and `body` with translated values. ```typescript posts = posts.map(post => post.translate()); ``` Output: ```json [ { "id": "1", "title": "ခေါင်းစဉ်", "body": "အကြောင်းအရာ" } ] ``` Use `TranslationConfig` to configure default options. You can use `TranslationConfig.use` many times (except for `entitySuffix`). ```typescript await appDataSource.initialize(); TranslationConfig.use({ getLocale: () => 'my', // default locale that is used when translating, typically from req.locale or als.getStore().locale getEntityManager: () => appDataSource.manager, // only needed for using with decorators entitySuffix: 'Translation', // only needed for using with decorators shouldDelete: true, // whether or not translations should be deleted after translation shouldMutate: false, // whether or not given entity should be mutated after translation }); ``` # Usage for repository extension (Experimental) This is rather overriding the repository instead of extending it. `TranslatableRepository` will override the `entityManager` of repository to left join translation entity automatically when doing select operations. ***Important** since `entityManager`'s `createQueryBuilder` will be overridden, you need to create new `entityManager`. You can reuse this `entityManager` for all repositories with translation. But not the repositories without translation. Not doing so will result in issues like [this](https://github.com/HanMoeHtet/typeorm-translatable/issues/2) . ```typescript const postManager = appDataSource.createEntityManager(); let postRepository = postManager.getRepository(Post); postRepository = postRepository.extend( TranslatableRepository(postManager) ); ``` Then when you query entities, just use normal repository methods. ```typescript let posts = await postRepository.find(); ``` This will output the same result as above. Also need to use `entity.translate` method for translation. # Caveats `TranslatableRepository.query` method will not left join. It will just execute the given query and return the result. # Usage with decorators (Experimental) You still need to extends the above mentioned classes. Classes decorated with `Translatable` wil generate a translation entity automatically. Class properties decorated with `TranslatableColumn` wil generate a column in the generated translation entity. Class property decorated with `Translations` will have one-to-many relation with the generated translation entity. ```typescript import { Translatable, TranslatableColumn, TranslatableEntity, Translation, TranslationEntity, Translations, } from 'typeorm-translatable'; import { Column, Entity, PrimaryGeneratedColumn, ObjectLiteral } from 'typeorm'; @Translatable() @Entity() export class PostWithDecorators extends TranslatableEntity< TranslationEntity<ObjectLiteral> > { @PrimaryGeneratedColumn({ type: 'bigint', unsigned: true }) id?: number; @TranslatableColumn() @Column('varchar') title?: string; @TranslatableColumn() @Column('text') body?: string; @Translations() translations?: Translation<TranslationEntity<ObjectLiteral>>[] | undefined; static translatableFields = new Set(['title', 'body']); } ``` Then translation entities need to be generated and need to be added into TypeORM `dataSourceOptions.entities`. Use `TranslationConfig.generate` method to generate and get translation entities. ```typescript entities: [ PostWithDecorators, ...TranslationConfig.generate(), ], ``` You can access the generated translation entity by using `getTranslationEntity` method. ```typescript const PostTranslation = TranslationConfig.getTranslationEntity( PostWithDecorators ); ``` See more examples [here](app/src/). # API Coming soon..., please read the source code for now :) # Credits - Inspired by framework [Vendure](https://github.com/vendure-ecommerce/vendure)