Skip to content

Commit

Permalink
JS: Converted/updated translation code to TS, fixed some comment counts
Browse files Browse the repository at this point in the history
- Migrated translation service to TS, stripping a lot of now unused code
  along the way.
- Added test to cover translation service.
- Fixed some comment count issues, where it was not showing correct
  value. or updating, on comment create or delete.
  • Loading branch information
ssddanbrown committed Oct 7, 2024
1 parent 8b9bcc1 commit d22413b
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 146 deletions.
4 changes: 2 additions & 2 deletions dev/docs/javascript-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,8 +137,8 @@ window.$events.showValidationErrors(error);
// Translator
// Take the given plural text and count to decide on what plural option
// to use, Similar to laravel's trans_choice function but instead
// takes the direction directly instead of a translation key.
window.trans_plural(translationString, count, replacements);
// takes the translation text directly instead of a translation key.
window.$trans.choice(translationString, count, replacements);

// Component System
// Parse and initialise any components from the given root el down.
Expand Down
12 changes: 3 additions & 9 deletions resources/js/app.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {EventManager} from './services/events.ts';
import {HttpManager} from './services/http.ts';
import Translations from './services/translations';
import {Translator} from './services/translations.ts';
import * as componentMap from './components';
import {ComponentStore} from './services/components.ts';

Expand All @@ -22,16 +22,10 @@ window.importVersioned = function importVersioned(moduleName) {
return import(importPath);
};

// Set events and http services on window
// Set events, http & translation services on window
window.$http = new HttpManager();
window.$events = new EventManager();

// Translation setup
// Creates a global function with name 'trans' to be used in the same way as the Laravel translation system
const translator = new Translations();
window.trans = translator.get.bind(translator);
window.trans_choice = translator.getPlural.bind(translator);
window.trans_plural = translator.parsePlural.bind(translator);
window.$trans = new Translator();

// Load & initialise components
window.$components = new ComponentStore();
Expand Down
2 changes: 1 addition & 1 deletion resources/js/components/page-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,9 @@ export class PageComment extends Component {
this.showLoading();

await window.$http.delete(`/comment/${this.commentId}`);
this.$emit('delete');
this.container.closest('.comment-branch').remove();
window.$events.success(this.deletedText);
this.$emit('delete');
}

showLoading() {
Expand Down
13 changes: 10 additions & 3 deletions resources/js/components/page-comments.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export class PageComments extends Component {

setupListeners() {
this.elem.addEventListener('page-comment-delete', () => {
this.updateCount();
setTimeout(() => this.updateCount(), 1);
this.hideForm();
});

Expand Down Expand Up @@ -72,7 +72,13 @@ export class PageComments extends Component {

window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => {
const newElem = htmlToDom(resp.data);
this.formContainer.after(newElem);

if (reqData.parent_id) {
this.formContainer.after(newElem);
} else {
this.container.append(newElem);
}

window.$events.success(this.createdText);
this.hideForm();
this.updateCount();
Expand All @@ -87,7 +93,8 @@ export class PageComments extends Component {

updateCount() {
const count = this.getCommentCount();
this.commentsTitle.textContent = window.trans_plural(this.countText, count, {count});
console.log('update count', count, this.container);
this.commentsTitle.textContent = window.$trans.choice(this.countText, count, {count});
}

resetForm() {
Expand Down
2 changes: 2 additions & 0 deletions resources/js/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import {ComponentStore} from "./services/components";
import {EventManager} from "./services/events";
import {HttpManager} from "./services/http";
import {Translator} from "./services/translations";

declare global {
const __DEV__: boolean;

interface Window {
$components: ComponentStore;
$events: EventManager;
$trans: Translator;
$http: HttpManager;
baseUrl: (path: string) => string;
}
Expand Down
67 changes: 67 additions & 0 deletions resources/js/services/__tests__/translations.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import {Translator} from "../translations";


describe('Translations Service', () => {

let $trans: Translator;

beforeEach(() => {
$trans = new Translator();
});

describe('choice()', () => {

test('it pluralises as expected', () => {

const cases = [
{
translation: `cat`, count: 10000,
expected: `cat`,
},
{
translation: `cat|cats`, count: 1,
expected: `cat`,
},
{
translation: `cat|cats`, count: 0,
expected: `cats`,
},
{
translation: `cat|cats`, count: 2,
expected: `cats`,
},
{
translation: `{0} cat|[1,100] dog|[100,*] turtle`, count: 0,
expected: `cat`,
},
{
translation: `{0} cat|[1,100] dog|[100,*] turtle`, count: 40,
expected: `dog`,
},
{
translation: `{0} cat|[1,100] dog|[100,*] turtle`, count: 101,
expected: `turtle`,
},
];

for (const testCase of cases) {
const output = $trans.choice(testCase.translation, testCase.count, {});
expect(output).toEqual(testCase.expected);
}
});

test('it replaces as expected', () => {
const caseA = $trans.choice(`{0} cat|[1,100] :count dog|[100,*] turtle`, 4, {count: '5'});
expect(caseA).toEqual('5 dog');

const caseB = $trans.choice(`an :a :b :c dinosaur|many`, 1, {a: 'orange', b: 'angry', c: 'big'});
expect(caseB).toEqual('an orange angry big dinosaur');
});

test('not provided replacements are left as-is', () => {
const caseA = $trans.choice(`An :a dog`, 5, {});
expect(caseA).toEqual('An :a dog');
});

});
});
131 changes: 0 additions & 131 deletions resources/js/services/translations.js

This file was deleted.

67 changes: 67 additions & 0 deletions resources/js/services/translations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/**
* Translation Manager
* Helps with some of the JavaScript side of translating strings
* in a way which fits with Laravel.
*/
export class Translator {

/**
* Parse the given translation and find the correct plural option
* to use. Similar format at Laravel's 'trans_choice' helper.
*/
choice(translation: string, count: number, replacements: Record<string, string> = {}): string {
const splitText = translation.split('|');
const exactCountRegex = /^{([0-9]+)}/;
const rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
let result = null;

for (const t of splitText) {
// Parse exact matches
const exactMatches = t.match(exactCountRegex);
if (exactMatches !== null && Number(exactMatches[1]) === count) {
result = t.replace(exactCountRegex, '').trim();
break;
}

// Parse range matches
const rangeMatches = t.match(rangeRegex);
if (rangeMatches !== null) {
const rangeStart = Number(rangeMatches[1]);
if (rangeStart <= count && (rangeMatches[2] === '*' || Number(rangeMatches[2]) >= count)) {
result = t.replace(rangeRegex, '').trim();
break;
}
}
}

if (result === null && splitText.length > 1) {
result = (count === 1) ? splitText[0] : splitText[1];
}

if (result === null) {
result = splitText[0];
}

return this.performReplacements(result, replacements);
}

protected performReplacements(string: string, replacements: Record<string, string>): string {
const replaceMatches = string.match(/:(\S+)/g);
if (replaceMatches === null) {
return string;
}

let updatedString = string;

for (const match of replaceMatches) {
const key = match.substring(1);
if (typeof replacements[key] === 'undefined') {
continue;
}
updatedString = updatedString.replace(match, replacements[key]);
}

return updatedString;
}

}

0 comments on commit d22413b

Please # to comment.